<!-- 聊天室 -->
<template>
  <div class="grid">
    <!-- 简言 -->
    <div class="flex col-12 justify-content-center align-content-center align-items-center">
      <MessageView icon="pi-flag"
                   class="absolute z-5 top-0">该公共聊天室是用于方便大家一起讨论的地方，在这里可以畅所欲言和志同道合的人一起交流。这里的记录只保留 24 小时哦(~●'◡'●)~
      </MessageView>
    </div>
    <!-- 头部 -->
    <div class="col-12">
      <CommonHeaderToolBar></CommonHeaderToolBar>
    </div>
    <!-- 整体聊天信息 -->
    <div ref="chatAndEditContainer" class="col-11 grid">
      <div class="col-12 pl-5 pr-5">
        <!-- 聊天容器 -->
        <div class="chat-container p-2 pl-3 pb-0" ref="chatContainer">
          <ChatPanel v-for="(item, index) of chatTranscript"
                     @img-click="imgClickHandler"
                     :key="index"
                     :center="item.center"
                     :right="item.right"
                     :left="item.left"
                     :avatar="item.avatar"
                     :name="item.nickname"
                     :context="item.context">
          </ChatPanel>
          <!-- 新消息点击 -->
          <div v-if="!autoScroll && values.newMsgCount !== 0"
               class="chat-tip p-2 flex align-content-center justify-content-center align-items-center"
               @click="goBottom">
          <span class="flex align-content-center justify-content-center align-items-center pr-3">
            <svg class="icon pt-1" viewBox="0 0 1024 1024" width="16" height="16"><path
                d="M493.504 558.144a31.904 31.904 0 0 0 45.28 0l308.352-308.352a31.968 31.968 0 1 0-45.248-45.248L516.16 490.272 221.984 196.128a31.968 31.968 0 1 0-45.248 45.248l316.768 316.768z"
                fill="#02c402"></path><path
                d="M801.888 460.576L516.16 746.304 222.016 452.16a31.968 31.968 0 1 0-45.248 45.248l316.768 316.768a31.904 31.904 0 0 0 45.28 0l308.352-308.352a32 32 0 1 0-45.28-45.248z"
                fill="#02c402"></path></svg>
          </span>
            <span
                class="pr-2"
                style="border-right: 1px solid rgba(114,104,104,0.36); color: #02c402">
           {{ values.newMsgCount }}条新的消息
          </span>
            <span class="flex align-content-center justify-content-center align-items-center pl-2">
            <svg viewBox="0 0 1024 1024" width="16" height="16">
              <path
                  d="M558.669 512l180.312 180.312-46.669 46.67L512 558.668 331.688 738.981l-46.67-46.669L465.332 512 285.019 331.688l46.669-46.67L512 465.332l180.312-180.312 46.67 46.669z">
            </path>
            </svg>
          </span>
          </div>
        </div>
      </div>
      <!-- 编辑器 -->
      <div class="col-12 pl-5 pr-5">
        <EditTextArea @input-value="editTextInputHandler"
                      @image-value="editTextImageHandler"
                      @on-enter-click="editTextEnterHandler"
                      :update-values="values.inputValue"
                      :update-images="values.imageValue"
                      :height="150"></EditTextArea>
      </div>
      <!-- 发送消息按钮 -->
      <div class="col-12 pl-5 pr-5 text-right">
        <Button label="发送"
                @click="editTextEnterHandler"
                :loading="btnLoading"></Button>
      </div>
    </div>
    <!-- 公共聊天室用户栏 -->
    <div class="col-1" :style="{maxHeight: `${maxHeight}px`}">
      <UserBubblesPanel v-for="(item, index) of chatUsers"
                        :key="index"
                        :fans-count="item.fansCount"
                        :focus="item.focus"
                        :avatar="item.avatar"
                        :desc="item.desc"
                        :nickname="item.nickname"
                        @user-focus="userFocusHandler">
        <template #container="{avatar, nickname}">
          <div class="col-12 flex user-msg"
               style="height: 50px;">
                  <span class="flex justify-content-start">
                    <Avatar v-if="avatar" :image="avatar"/>
                    <Avatar v-else :label="nickname.substring(0,1).toUpperCase()"/>
                  </span>
            <span class="flex justify-content-end user-msg-nickname" style="font-size: 18px; text-overflow:ellipsis;">
              {{ nickname }}
            </span>
          </div>
        </template>
      </UserBubblesPanel>
    </div>
    <!-- 图片预览 -->
    <div v-show="imageHandler.preview"
         class="p-image-mask p-component-overlay p-component-overlay-enter"
         style="z-index: 1104;">
      <div class="p-image-toolbar">
        <button @click="imageHandler.rotate+=90;" class="p-image-action p-link" type="button"><i
            class="pi pi-refresh"></i></button>
        <button @click="imageHandler.rotate-=90;" class="p-image-action p-link" type="button"><i class="pi pi-undo"></i>
        </button>
        <button @click="imageHandler.scale-=0.1;" class="p-image-action p-link" type="button"><i
            class="pi pi-search-minus"></i></button>
        <button @click="imageHandler.scale+=0.1;" class="p-image-action p-link" type="button"><i
            class="pi pi-search-plus"></i></button>
        <button @click="closePreview" class="p-image-action p-link" type="button"><i
            class="pi pi-times"></i></button>
      </div>
      <div id="image__preview" class="">
        <img :src="imageHandler.src"
             class="p-image-preview p-5"
             :style="`transform: rotate(${imageHandler.rotate}deg) scale(${imageHandler.scale});`">
      </div>
    </div>
  </div>
</template>

<script lang="ts" setup>
/** JS */
import {computed, inject, nextTick, onBeforeUnmount, onMounted, reactive, ref, Ref, watch} from 'vue';
import Cache from '@/utils/Cache';
import {UserMsg} from '@/interface/user/entity/UserEntityInterface';
import {Websocket} from '@/utils/Webscoket';
import {Message, MessageTypes} from '@/interface/websocket/entity/SocketMessage';
import {FileService, QuestionResponse, UserService} from '@/utils/ApiRequest';
import toast from '@/utils/Toast';

/** 组件 */
import CommonHeaderToolBar from '@/components/toolbar/CommonHeaderToolBar.vue';
import EditTextArea from '@/components/input/EditTextarea.vue';
import UserBubblesPanel from '@/components/panel/UserBubblesPanel.vue';
import ChatPanel from '@/components/panel/ChatPanel.vue';
import MessageView from 'primevue/message';

/** 用户筛选数据 */
type ChatUserMsg = {
  nickname: string
  avatar: string
  desc: string
}
/** 消息类型 */
type ChatMessage = {
  center?: boolean
  left?: boolean
  avatar?: string
  right: boolean
  nickname: string
  context: string
}
/** 消息通知 */
type ChatMessageNotice = {
  from?: ChatUserMsg
  to?: string
  text?: string
  publishTime?: Date
}
/** 聊天室信息 */
type ChatroomInfo = {
  name: string
  transcript: ChatMessageNotice[]
  userMessages: UserMsg[]
}


/** 初始化构造对象 */
const cache = new Cache(),
    $ = require('jquery'),
    websocket: Websocket | undefined = inject('websocket'),
    messages: Array<(event) => void> | undefined = inject('messages'),
    userMsg: UserMsg = cache.get('userMsg'),
    fileService = new FileService(),
    userService = new UserService();
/** 元素钩子 */
const chatContainer = ref(null), chatAndEditContainer = ref(null);

/** 气泡框关注反馈 */
const userFocusHandler = (val) => {
  let chatUserMsgList: UserMsg[] = chatUsers.value;
  let {nickname, isFocus} = val;
  for (let tempChatUserMsg of chatUserMsgList) {
    if (tempChatUserMsg.nickname === nickname) {
      tempChatUserMsg.focus = isFocus;
      if (tempChatUserMsg.fansCount !== undefined) {
        if (isFocus) tempChatUserMsg.fansCount += 1;
        else tempChatUserMsg.fansCount -= 1;
      }
    }
  }
}

/** 滑动至聊天框底部 */
function goBottom(behavior: boolean) {
  nextTick(() => {
    if (chatContainer.value) {
      let element = (chatContainer.value as HTMLElement);
      let scrollHeight = element.scrollHeight, clientHeight = element.clientHeight;
      element.scrollTo({top: scrollHeight + clientHeight, behavior: behavior ? 'smooth' : 'auto'})
    }
    if (values.newMsgCount != 0) {
      values.newMsgCount = 0;
    }
  })
}

/** 聊天框数据处理 */
let values = reactive({
  inputValue: '',
  imageValue: [] as any[],
  newMsgCount: 0
})
/** 聊天处理 */
// 新消息自动滚动
const autoScroll = ref(true), btnLoading = ref(false);
// 聊天信息与用户信息
const chatTranscript: Ref<ChatMessage[]> = ref([]), chatUsers: Ref<UserMsg[]> = ref([]);
const sendMsg = (right: boolean, message: { nickname: string, context: string, avatar?: string }) => {
      let context = message.context;
      if (message && (context && context.trim() !== '')) {
        let msg: ChatMessage = {
          center: false,
          right: right,
          left: !right,
          avatar: message.avatar,
          nickname: message.nickname,
          context: message.context
        }
        chatTranscript.value.push(msg);
      }
    },
    sendLeave = (nickname: string) => {
      let msg: ChatMessage = {
        center: true,
        right: false,
        left: false,
        nickname: nickname,
        context: `${nickname} 离开了聊天室`
      }
      chatTranscript.value.push(msg);
    };

const saveImages = async () => {
  let formData = values.imageValue;
  const $edit = $('#yzj__text_area');
  if (formData.length > 0) {
    let fd = new FormData();
    for (let image of formData) {
      fd.append('files', image.get('file'));
    }
    let res = await fileService.uploadChatImage(fd);
    if (res) {
      let images = $edit.find('img');
      for (let i = 0; i < images.length; i++) {
        $(images[i]).attr('src', res[i]);
        $(images[i]).removeAttr('width');
        $(images[i]).css({maxWidth: '200px', borderRadius: '5px', cursor: 'pointer'});
      }
      // 清空 images
      values.imageValue = [];
    }
    return $edit.html()
  }
}
const editTextInputHandler = (val) => {
      values.inputValue = val;
    },
    editTextImageHandler = (val) => {
      // 此处已经获取到了表单
      values.imageValue = val;
    },
    editTextEnterHandler = async () => {
      if (values.inputValue.trim() == '') {
        toast.warn('喂喂喂', '内容不能为空的呀~老糊涂了你', 1500);
      }
      // 发送聊天信息
      btnLoading.value = true;
      let text = await saveImages();
      if (text && text.trim() !== '') {
        values.inputValue = text;
      }
      sendMsg(true, {nickname: userMsg.nickname, context: values.inputValue, avatar: userMsg.avatar});
      let msgNotice: ChatMessageNotice = {
        from: userMsg,
        text: values.inputValue
      }
      await websocket?.send({type: MessageTypes.CommonChat, context: msgNotice});
      btnLoading.value = false;
      values.inputValue = '';
      goBottom(true);
    };
// 点击图片
const imageHandler = reactive({
  preview: false,
  src: '',
  rotate: 0,
  scale: 0.9,
});
const imgClickHandler = async (ev) => {
  imageHandler.preview = true;
  imageHandler.src = ev.src;
}, closePreview = () => {
  imageHandler.preview = false;
  imageHandler.rotate = 0;
  imageHandler.scale = 0.9;
}
document.onkeydown = function (e) {
  if (e.code === 'Escape' && imageHandler.preview) {
    closePreview()
  }
}
// watch(chatUsers, (n) => {
//   console.log('n->', n);
// }, {deep: true})
/**
 * 监听信息回调
 */
const autoScrollHandler = () => {
  // 没有滚动高度 autoScroll 设置为 false
  if (chatContainer.value && !autoScroll.value) {
    let element = (chatContainer.value as HTMLElement);
    if (element.scrollHeight == element.clientHeight) {
      autoScroll.value = true;
    }
  }
  if (autoScroll.value) {
    goBottom(true);
  }
}
messages?.push(async (res) => {
  let result: Message = JSON.parse(res.data);
  // 公共聊天室
  if (result.type === MessageTypes.CommonChat) {
    if (result.context) {
      let notice: any = JSON.parse(result.context) as any;
      // 聊天室的记录和用户信息
      let transcript, userMessages;
      // 聊天记录和聊天室用户
      if ((userMessages = notice.userMessages) && userMessages.length >= 0) {
        // 补充用户信息
        let arr = [] as string[]
        for (let userMessage of userMessages) {
          arr.push(userMessage.nickname);
        }
        let res = await userService.getUserMsgList(arr);
        chatUsers.value = res.data;

        // 遍历补充聊天信息
        if ((transcript = notice.transcript) && transcript.length >= 0) {
          for (let noticeElement of transcript) {
            let right = noticeElement.from.nickname == userMsg.nickname;
            sendMsg(right, {
              nickname: noticeElement.from.nickname,
              avatar: noticeElement.from.avatar,
              context: noticeElement.text
            });
            goBottom(false);
          }
        }
      } else if (notice.from && notice.text === undefined) {
        // 用户进入
        let tempUserMsg = await userService.getUserMsg(notice.from.nickname);
        chatUsers.value.push(tempUserMsg.data);
      }
      // 获取对方发送的消息
      else if (notice.text && notice.from) {
        sendMsg(false, {nickname: notice.from.nickname, avatar: notice.from.avatar, context: notice.text});
        values.newMsgCount++;
        // 没有滚动高度 autoScroll 设置为 false
        autoScrollHandler();
      }
    }
  } else if (result.type === MessageTypes.ExitCommonChat) {
    if (result.context) {
      // 被通知有用户离开聊天室
      values.newMsgCount++;
      let chatUserMsg: ChatUserMsg = JSON.parse(result.context).from as ChatUserMsg;
      let index = 0;
      for (let chatUser of chatUsers.value) {
        if (chatUser.nickname == chatUserMsg.nickname) {
          sendLeave(chatUser.nickname);
          autoScrollHandler();
          break;
        }
        index++;
      }
      chatUsers.value.splice(index, 1);
    }
  }
});
/** 公共聊天室用户 */
const maxHeight = ref(0);
onMounted(() => {
  nextTick(() => {
    // 连接公共聊天室
    let chatUserMsg: ChatUserMsg = {
      nickname: userMsg.nickname,
      avatar: userMsg.avatar,
      desc: userMsg.desc
    }
    websocket?.send({type: MessageTypes.CommonChat, context: {from: chatUserMsg}})
    // 监听聊天框
    if (chatContainer.value) {
      let element = (chatContainer.value as HTMLElement);
      // 没有滚动高度 autoScroll 设置为 true
      if (element.scrollHeight == element.clientHeight) {
        autoScroll.value = true;
      }
      document.addEventListener('scroll', (e) => {
        nextTick(() => {
          if (element) {
            let scrollHeight = element.scrollHeight, scrollTop = element.scrollTop, clientHeight = element.clientHeight;
            let ceilTop = Math.ceil(scrollTop + clientHeight);
            if (ceilTop >= scrollHeight - 100) {
              // 到底部了
              values.newMsgCount = 0;
            } else if (element.scrollHeight != element.clientHeight) {
              // 判断滚动
              autoScroll.value = (scrollTop >= (scrollHeight - clientHeight) / 2) && values.newMsgCount == 0;
            }
          }
        })
      }, true);
    }
    // 获取聊天框和输入框的整体高度，并复制给用户栏
    if (chatAndEditContainer.value) {
      let element = chatAndEditContainer.value as HTMLElement;
      maxHeight.value = element.clientHeight;
    }
  })
});
onBeforeUnmount(() => {
  // 退出聊天室
  let chatUserMsg: ChatUserMsg = {
    nickname: userMsg.nickname,
    avatar: userMsg.avatar,
    desc: userMsg.desc
  }
  websocket?.send({type: MessageTypes.ExitCommonChat, context: {from: chatUserMsg}})
})
</script>

<style lang="less" scoped>
.user-msg {
  &:hover {
    cursor: pointer;
    color: red;
  }
}

.chat-tip {
  &:hover {
    cursor: pointer;
  }

  float: right;
  position: sticky;
  bottom: 0;
  background-color: rgba(255, 255, 255, 0.99);
  color: black;
  padding: 5px;
  border-top-left-radius: 5px;
  border-top-right-radius: 5px;
}

.chat-container {
  background-color: #f5f5f5;
  height: 55vh;
  overflow-y: scroll;
  position: relative;
}

.p-message-wrapper {
  padding: 0px !important;
}
</style>
